﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using ParieurPro.DAL.DatabaseModel;
using System.Data.SqlClient;
using System.Text;
using System.Security.Cryptography;

namespace ParieurPro.DAL
{
    /// <summary>
    /// Classe de gestion des tournois de paris sur une ou des compétitions.
    /// Un tournoi peut porter sur un ensemble de compétition (Ligue 1 2011/12 et Ligue 2 2011/12 par exemple)
    /// Un tournoi est constitué d'un identifiant unique, d'un nom (donné par l'administrateur du site) et
    /// d'un mot de passe
    /// </summary>
    [Serializable()]
    public class CustomContestObject
    {

        #region Enum
        /// <summary>
        /// Statuts de l'inscription à un tournoi personnalisé :
        /// - Success : Utilisateur inscrit
        /// - AlreadySubscribe : Utilisateur déjà inscrit
        /// - NotAllowed : Utilisateur non autorisé
        /// - GeneralFailure : Erreur interne
        /// </summary>
        public enum JoinContestStatus
        {
            Success = 0,
            AlreadySubscribe = 1,
            NotAllowed = 2,
            GeneralFailure = 100
        }

        public enum CreateContestStatus
        {
            Success = 0,
            GeneralFailure = 100
        }

        public enum DeleteContestStatus
        {
            Success = 0,
            GeneralFailure = 100
        }

        public enum DisjoinContestStatus
        {
            Success = 0,
            NotSubscribe = 1,
            GeneralFailure = 100
        }
        #endregion

        #region Attributes
        private List<ContestObject> _contestList;
        private string _displayName;
        private string _password;
        private int _id;
        private Guid _administratorId;
        private int _subscriberCount;
        private int? _currentRank;
        private byte? _isDefaultContest;
        #endregion

        #region Properties
        /// <summary>
        /// Identifiant unique du tournoi
        /// </summary>
        [Key()]
        [DataMemberAttribute()]
        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }

        /// <summary>
        /// Liste des compétitions sur lesquelles portent
        /// le tournoi
        /// </summary>
        [DataMemberAttribute()]
        public List<ContestObject> ContestList
        {
            get { return _contestList; }
            set { _contestList = value; }
        }

        /// <summary>
        /// Nom du tournoi
        /// </summary>
        [DataMemberAttribute()]
        public string DisplayName
        {
            get { return _displayName; }
            set { _displayName = value; }
        }

        /// <summary>
        /// Mot de passe du tournoi
        /// </summary>
        [DataMemberAttribute()]
        public string Password
        {
            get { return _password; }
            set { _password = value; }
        }
        
        /// <summary>
        /// Identifiant de l'administrateur
        /// </summary>
        public Guid AdministratorId
        {
            get { return _administratorId; }
            set { _administratorId = value; }           
        }

        [DataMemberAttribute()]
        public int SubscriberCount
        {
            get { return _subscriberCount; }
            set { _subscriberCount = value; }
        }

        [DataMemberAttribute()]
        public int? CurrentRank
        {
            get { return _currentRank; }
            set { _currentRank = value; }
        }

        [DataMemberAttribute()]
        public byte? IsDefaultContest
        {
            get { return _isDefaultContest; }
            set { _isDefaultContest = value; }
        }

        #endregion

        #region Data access

        /// <summary>
        /// Copie l'objet représentant la table "CustomContest" de la base de données
        /// en objet "CustomContestObject"
        /// </summary>
        /// <param name="dataToCopy">Objet à copier</param>
        /// <returns>Objet copié</returns>
        public static CustomContestObject CopyFromDatabaseObject(CustomContest dataToCopy)
        {
            if (dataToCopy == null)
            {
                throw new ArgumentException("CustomContestObject.CopyFromDatabaseObject - dataToCopy can not be null");
            }
            CustomContestObject returnObject = new CustomContestObject();
            returnObject.Id = dataToCopy.CustomContestId;
            returnObject.DisplayName = dataToCopy.DisplayName;
            returnObject.Password = dataToCopy.Password;
            returnObject.AdministratorId = dataToCopy.AdministratorId;
            return returnObject;
        }

        /// <summary>
        /// Renvoie la liste des tournois autorisés pour l'utilisateur donné. Au minimum le tournoi par défaut du
        /// site ParieurPro.com.
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="userEmail">Adresse mail de l'utilisateur</param>
        /// <returns>Liste des tournois autorisés</returns>
        public static List<CustomContestObject> GetAllowedContests(MyBetDBEntities dbContext, string userEmail)
        {
            if (dbContext == null)
            {
                throw new ArgumentException("CustomContestObject.GetAllowedContests - dbContext can not be null");
            }
            if (String.IsNullOrEmpty(userEmail))
            {
                throw new ArgumentException("CustomContestObject.GetAllowedContests - userEmail can not be null or empty");
            }
            List<CustomContestObject> customContestList = new List<CustomContestObject>() ;
            var query1 = (from contest in dbContext.CustomContest
                         where contest.IsDefault==true
                         select contest);
            List<CustomContest> contests = query1.ToList();

            foreach (CustomContest cc in contests)
            {
                customContestList.Add(CustomContestObject.CopyFromDatabaseObject(cc));
            }

            var query2 = (from contest in dbContext.CustomContest
                         from contestLink in dbContext.UserMailCustomContest
                         where (contestLink.CustomContestId == contest.CustomContestId) && (contestLink.LoweredUserEmail == userEmail.ToLower())
                         select contest);
            contests = query2.ToList();

            foreach (CustomContest currentCustomContest in contests)
            {
                customContestList.Add(CustomContestObject.CopyFromDatabaseObject(currentCustomContest));
            }
            return customContestList;
        }

        /// <summary>/// Calculates SHA1 hash
        /// </summary>
        /// <param name="text">input string</param>
        /// <param name="enc">Character encoding</param>
        /// <returns>SHA1 hash</returns>
        private static string CalculateSHA1(string text, Encoding enc)
        {
            byte[] buffer = enc.GetBytes(text);
            SHA1CryptoServiceProvider cryptoTransformSHA1 = new SHA1CryptoServiceProvider();
            string hash = BitConverter.ToString(cryptoTransformSHA1.ComputeHash(buffer)).Replace("-", "");
            return hash;
        }

        /// <summary>
        /// Fonction pour qu'un utilisateur rejoigne un tournoi.
        /// L'utilisateur doit être autorisé à rejoindre le tournoi.
        /// L'utilisateur ne doit pas déjà être inscrit au tournoi.
        /// </summary>
        /// <param name="dbContext">Connection à la BD</param>
        /// <param name="userId">Identifiant de l'utilisateur qui rejoint le tournoi</param>
        /// <param name="customContestName">Nom du tournoi personnalisé à rejoindre</param>
        /// <param name="customContestPassword">Mot de passe du tournoi personnalisé à rejoindre</param>
        /// <returns>Success si l'inscription a réussi, un statut d'erreur sinon</returns>
        /// <exception cref="ArgumentException">dbContext vaut null</exception>
        public static JoinContestStatus JoinCustomContest(MyBetDBEntities dbContext, Guid userId, string customContestName, string customContestPassword)
        {
            int returnValue = -1;
            string password = CalculateSHA1(customContestPassword,Encoding.ASCII);

            if (dbContext == null)
            {
                throw new ArgumentException("CustomContestObject.JoinCustomContest - dbContext can not be null");
            }
            try
            {
                // Exécution de la procédure stockée pour rejoindre un tournoi
                //returnValue = dbContext.ExecuteFunction("exec mybet_joinContest @UserId={0}, @CustomContestId={1}", userId, customContestId);
                object[] parameters = 
                        {
                           new SqlParameter { ParameterName = "p0", Value = userId },
                           new SqlParameter { ParameterName = "p1", Value = customContestName },
                           new SqlParameter { ParameterName = "p2", Value = password }
                        };

                IEnumerable<int> queryResult = dbContext.ExecuteStoreQuery<int>
                (String.Format(@"DECLARE @return_value int
                                 EXEC @return_value = [dbo].[mybet_joinContest]
		                         @UserId = @p0,
		                         @CustomContestName = @p1,
                                 @CustomContestPassword = @p2
                                 SELECT	@return_value"), parameters);

                returnValue = queryResult.FirstOrDefault();

                switch (returnValue)
                {
                    case 0:
                        // Succes
                        return JoinContestStatus.Success;
                    case -8:
                        // Utilisateur déjà inscrit
                        return JoinContestStatus.AlreadySubscribe;
                    case -9:
                        // Utilisateur déjà inscrit
                        return JoinContestStatus.NotAllowed;
                    case -7:
                        // Utilisateur non autorisé
                        return JoinContestStatus.NotAllowed;
                    default:
                        // Erreur SQL interne
                        throw new System.Exception(String.Format("JoinCustomContest - Internal Error : {0}", returnValue));
                }
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }
            return JoinContestStatus.GeneralFailure;
        }

        /// <summary>
        /// Fonction pour qu'un utilisateur quitte un tournoi.
        /// L'utilisateur doit être inscrit au tournoi.
        /// </summary>
        /// <param name="dbContext">Connection à la BD</param>
        /// <param name="userId">Identifiant de l'utilisateur qui quitte le tournoi</param>
        /// <param name="customContestId">Identifiant du tournoi personnalisé à quitter</param>
        /// <returns>Success si la désinscription a réussi, un statut d'erreur sinon</returns>
        /// <exception cref="ArgumentException">dbContext vaut null</exception>
        public static DisjoinContestStatus DisjoinCustomContest(MyBetDBEntities dbContext, Guid userId, int customContestId)
        {
            int returnValue = -1;
            if (dbContext == null)
            {
                throw new ArgumentException("CustomContestObject.DisjoinCustomContest - dbContext can not be null");
            }
            try
            {
                // Exécution de la procédure stockée pour rejoindre un tournoi
                object[] parameters = 
                        {
                           new SqlParameter { ParameterName = "p0", Value = userId },
                           new SqlParameter { ParameterName = "p1", Value = customContestId }
                        };

                IEnumerable<int> queryResult = dbContext.ExecuteStoreQuery<int>
                (String.Format(@"DECLARE @return_value int
                                 EXEC @return_value = [dbo].[mybet_disjoinContest]
		                         @UserId = @p0,
		                         @CustomContestId = @p1 
                                 SELECT	@return_value"), parameters);

                returnValue = queryResult.FirstOrDefault();

                switch (returnValue)
                {
                    case 0:
                        // Succes
                        return DisjoinContestStatus.Success;
                    case -7:
                        return DisjoinContestStatus.NotSubscribe;
                    default:
                        // Erreur SQL interne
                        throw new System.Exception(String.Format("DisjoinCustomContest - Internal Error : {0}", returnValue));
                }
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }

            return DisjoinContestStatus.GeneralFailure;

        }

        /// <summary>
        /// Renvoie la liste des identifiants des tournois abonnés à une compétition donnée
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="contestId">Identifiant de la compétition</param>
        /// <returns>Liste des identifiants</returns>
        public static List<int> GetCustomContestsIdListFromContestId(MyBetDBEntities dbContext, int contestId)
        {
            if (dbContext == null)
            {
                throw new ArgumentException("CustomContestObject.GetCustomContestsIdListFromContestId - dbContext can not be null");
            }
            
            List<CustomContestObject> customContestList = new List<CustomContestObject>() ;
            object[] parameters = 
            {
                new SqlParameter { ParameterName = "p1", Value = contestId }
            };

            List<int> queryResult = dbContext.ExecuteStoreQuery<int>
                (@"SELECT ccl.CustomContestId FROM [CustomContestLinks] ccl
                    where ccl.ContestId=@p1 order by ccl.CustomContestId;", parameters).ToList();

            return queryResult;
        }

        /// <summary>
        /// Renvoie l'identifiant du tournoi dont le nom est passé en paramètre
        /// </summary>
        /// <param name="dbContext">Connexion à la base de données</param>
        /// <param name="contestName">Nom du tournoi</param>
        /// <returns>Identifiant du tournoi, 0 s'il n'existe pas</returns>
        public static int GetCustomContestsIdFromName(MyBetDBEntities dbContext, string contestName)
        {
            if (dbContext == null)
            {
                throw new ArgumentException("CustomContestObject.GetCustomContestsIdFromName - dbContext can not be null");
            }
            object[] parameters = 
            {
                new SqlParameter { ParameterName = "p1", Value = contestName.Trim().ToUpper() }
            };

            int query1 = (from contest in dbContext.CustomContest
                          where contest.DisplayName.ToUpper() == contestName.ToUpper()
                          select contest.CustomContestId).FirstOrDefault();

            return query1;
        }
        #endregion

        #region CustomContest management

        /// <summary>
        /// Création d'un nouveau tournoi personnalisé
        /// </summary>
        /// <param name="dbContext">Connexion à la BDD</param>
        /// <param name="userId">Administrateur du tournoi</param>
        /// <param name="contestId">Identifiant du championnat à personnaliser</param>
        /// <param name="customContestName">Nom du tournoi personnalisé</param>
        /// <param name="password">Mot de passe du tournoi</param>
        /// <returns></returns>
        public static CreateContestStatus CreateCustomContest(MyBetDBEntities dbContext, 
                                                            Guid userId,
                                                            int contestId,
                                                            string customContestName,
                                                            string password)
        {
            int returnValue = -1;
            //if (dbContext == null)
            //{
            //    throw new ArgumentException("CustomContestObject.CreateCustomContest - dbContext can not be null");
            //}
            try
            {
                // Exécution de la procédure stockée pour créer le tournoi
                object[] parameters = 
                        {
                           new SqlParameter { ParameterName = "p0", Value = userId },
                           new SqlParameter { ParameterName = "p1", Value = contestId },
                           new SqlParameter { ParameterName = "p2", Value = customContestName },
                           new SqlParameter { ParameterName = "p3", Value = password }
                        };

                IEnumerable<int> queryResult = dbContext.ExecuteStoreQuery<int>
                (String.Format(@"DECLARE @return_value int
                                 EXEC @return_value = [dbo].[mybet_createCustomContest]
		                         @UserId = @p0,
		                         @ContestId = @p1,
                                 @DisplayName = @p2,
                                 @Password = @p3
                                 SELECT	@return_value"), parameters);

                returnValue = queryResult.FirstOrDefault();

                switch (returnValue)
                {
                    case 0:
                        // Succes
                        return CreateContestStatus.Success;
                    default:
                        // Erreur SQL interne
                        throw new System.Exception(String.Format("CreateCustomContest - Internal Error : {0}", returnValue));
                }
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }
            return CreateContestStatus.GeneralFailure;
        }

        /// <summary>
        /// Suppression d'un tournoi personnalisé
        /// </summary>
        /// <param name="dbContext">Connexion à la BDD</param>
        /// <param name="userId">Administrateur du tournoi</param>
        /// <param name="customContestId">Identifiant du championnat à personnaliser</param>
        /// <returns></returns>
        public static DeleteContestStatus DeleteCustomContest(MyBetDBEntities dbContext,
                                                              Guid userId,
                                                              int customContestId)
        {
            int returnValue = -1;
            if (dbContext == null)
            {
                throw new ArgumentException("CustomContestObject.DeleteCustomContest - dbContext can not be null");
            }
            try
            {
                // Exécution de la procédure stockée pour créer le tournoi
                object[] parameters = 
                        {
                           new SqlParameter { ParameterName = "p0", Value = userId },
                           new SqlParameter { ParameterName = "p1", Value = customContestId }
                        };

                IEnumerable<int> queryResult = dbContext.ExecuteStoreQuery<int>
                (String.Format(@"DECLARE @return_value int
                                 EXEC @return_value = [dbo].[mybet_deleteCustomContest]
		                         @UserId = @p0,
		                         @CustomContestId = @p1
                                 SELECT	@return_value"), parameters);

                returnValue = queryResult.FirstOrDefault();

                switch (returnValue)
                {
                    case 0:
                        // Succes
                        return DeleteContestStatus.Success;
                    default:
                        // Erreur SQL interne
                        throw new System.Exception(String.Format("DeleteCustomContest - Internal Error : {0}", returnValue));
                }
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }
            return DeleteContestStatus.GeneralFailure;
        }

        #endregion

    }
}